package com.hmy.es.config;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.CollectionUtils;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.List;

@Getter
@Setter
@Log4j2
public class EsProperty {

    /**
     * 连接配置
     * e.g. 127.0.0.1:9200,127.0.0.1:9201
     */
    private String uris;

    @Deprecated
    private String host = "127.0.0.1";

    @Deprecated
    private Integer port = 9200;

    /** 开启鉴权 */
    private Boolean authEnable = false;

    /** 用户名 */
    private String username = "elastic";

    /** 密码 */
    private String password;

    /** 开启证书认证 */
    private Boolean caAuthEnable = false;

    /** 证书路径 - resources目录下 */
    private String caPath;

    /** 证书认证密码 */
    private String caPassword;

    /**
     * 连接超时时间
     */
    private int connectTimeout = RestClientBuilder.DEFAULT_CONNECT_TIMEOUT_MILLIS;

    /**
     * Socket 连接超时时间
     */
    private int socketTimeout = RestClientBuilder.DEFAULT_SOCKET_TIMEOUT_MILLIS;


    public List<HttpHost> getHttpHost() {
        List<HttpHost> httpHosts = new ArrayList<>();
        for (String uri : uris.split(",")) {
            uri = uri.trim();
            if (StringUtils.isBlank(uri) || uri.split(":").length != 2) {
                continue;
            }
            try {
                String[] address = uri.split(":");
                httpHosts.add(new HttpHost(address[0], Integer.parseInt(address[1]), "http"));
            } catch (NumberFormatException e) {
                log.error("es配置出错， exception:{}", ExceptionUtils.getStackTrace(e));
            }

        }
        return httpHosts;
    }

    private SSLContext buildSSLContext() {
        ClassPathResource resource = new ClassPathResource(caPath);
        SSLContext sslContext = null;
        try {
            KeyStore ks = KeyStore.getInstance("PKCS12");
            ks.load(resource.getInputStream(), StringUtils.isBlank(caPassword) ? null : caPassword.toCharArray());
            SSLContextBuilder sslContextBuilder = SSLContexts.custom()
                    .loadTrustMaterial(ks, null);
            sslContext = sslContextBuilder.build();
        } catch (CertificateException | IOException | KeyStoreException | NoSuchAlgorithmException |
                 KeyManagementException e) {
            log.error("ES连接认证失败", e);
        }

        return sslContext;
    }

    public ElasticsearchClient getElasticsearchClient() {
        List<HttpHost> httpHost = getHttpHost();
        if (CollectionUtils.isEmpty(httpHost)) {
            return null;
        }

        RestClient restClient;
        // 无需鉴权
        if (!authEnable || StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
            restClient = RestClient
                    .builder(httpHost.toArray(new HttpHost[0]))
                    .build();
        } else {
            // 配置用户名&密码
            final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
            credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));

            RestClientBuilder restClientBuilder = RestClient.builder(httpHost.toArray(new HttpHost[0]));

            // 配置超时时间
            restClientBuilder.setRequestConfigCallback(rcc ->
                    rcc.setConnectTimeout(connectTimeout)
                    .setSocketTimeout(socketTimeout)
            );

            // 自签证书的设置，并且还包含了账号密码
            if (caAuthEnable && StringUtils.isNotBlank(caPath)) {
                RestClientBuilder.HttpClientConfigCallback callback = httpAsyncClientBuilder -> httpAsyncClientBuilder
                        .setSSLContext(buildSSLContext())
                        .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
                        .setDefaultCredentialsProvider(credentialsProvider);
                restClientBuilder.setHttpClientConfigCallback(callback);
            }
            // 用builder创建RestClient对象
            restClient = restClientBuilder.build();
        }

        // 使用 Jackson 映射器创建传输
        ElasticsearchTransport transport = new RestClientTransport(
                restClient, new JacksonJsonpMapper());
        return new ElasticsearchClient(transport);
    }
}
